// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.

namespace Microsoft.Samples.Commerce.Order.CommerceEntities
{
    using System;
    using Microsoft.Commerce.Contracts;

    /// <summary>
    /// Represents a shopper's basket (cart) or an order.
    /// </summary>
    public class Basket : ICommerceEntity
    {
        /// <summary>
        /// defines the model name for this commerce entity
        /// </summary>
        public const string ModelNameDefinition = "Basket";

        /// <summary>
        /// The commerce entity.
        /// </summary>
        private CommerceEntity commerceEntity;

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Basket"/> class
        /// </summary>
        public Basket()
        {
            this.commerceEntity = new CommerceEntity(Basket.ModelNameDefinition);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Basket"/> class
        /// </summary>
        /// <param name="commerceEntity">The commerce entity.</param>
        public Basket(CommerceEntity commerceEntity)
        {
            this.commerceEntity = commerceEntity;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Basket"/> class.
        /// </summary>
        /// <param name="name">The name of the Basket.</param>
        /// <param name="userId">The id of the user who's basket this is.</param>
        /// <param name="basketType">Type of the basket.</param>
        public Basket(string name, string userId, int basketType)
            : this()
        {
            this.Name = name;
            this.UserId = userId;
            this.BasketType = basketType;
        }

        #endregion

        #region ICommerceEntity Members

        /// <summary>
        /// Gets or sets the id.
        /// </summary>
        /// <value>The id of the entity.</value>
        public string Id
        {
            get { return this.commerceEntity.Id; }
            set { this.commerceEntity.Id = value; }
        }

        /// <summary>
        /// Gets or sets the type name of this object instance
        /// <remarks>Required so that the Builders can convert to the Multi-channel Commerce Foundation CommerceEntity base type</remarks>
        /// </summary>
        /// <value></value>
        public string ModelName
        {
            get { return this.commerceEntity.ModelName; }
            set { this.commerceEntity.ModelName = value; }
        }

        /// <summary>
        /// Gets or sets this objects collection of Properties and their corresponding values
        /// </summary>
        /// <value></value>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Collection setting is required")]
        public CommercePropertyCollection Properties
        {
            get { return this.commerceEntity.Properties; }
            set { this.commerceEntity.Properties = value; }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the collection of addresses for the current basket.
        /// </summary>
        /// <value>The addresses.</value>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Collection setting is required")]
        public CommerceRelationshipList Addresses
        {
            get { return this.commerceEntity.GetPropertyValue(RelationshipName.Addresses) as CommerceRelationshipList; }
            set { this.commerceEntity.SetPropertyValue(RelationshipName.Addresses, value); }
        }

        /// <summary>
        /// Gets or sets the total of the amounts of discounts that apply to multiple items in the Basket.
        /// </summary>
        /// <value>The discount total.</value>
        public decimal? BasketLevelDiscountsTotal
        {
            get { return (decimal?)this.commerceEntity.GetPropertyValue(PropertyName.BasketLevelDiscountsTotal); }
            set { this.commerceEntity.SetPropertyValue(PropertyName.BasketLevelDiscountsTotal, value ?? null); }
        }

        /// <summary>
        /// Gets or sets the basket type.
        /// </summary>
        /// <value>The type of the basket.</value>
        public int? BasketType
        {
            get { return (int?)this.commerceEntity.GetPropertyValue(PropertyName.BasketType); }
            set { this.commerceEntity.SetPropertyValue(PropertyName.BasketType, value ?? null); }
        }

        /// <summary>
        /// Gets or sets the billing currency.
        /// </summary>
        /// <value>The billing currency.</value>
        public string BillingCurrency
        {
            get { return this.commerceEntity.GetPropertyValue(PropertyName.BillingCurrency) as string; }
            set { this.commerceEntity.SetPropertyValue(PropertyName.BillingCurrency, value); }
        }

        /// <summary>
        /// Gets or sets the total of discounts, which is the sum of LineItem, BasketLevel and Shipping discount totals
        /// </summary>
        /// <value>The cross sell products.</value>
        /// <remarks>This is an optional property.</remarks>
        public decimal? DiscountsTotal
        {
            get { return (decimal?)this.commerceEntity.GetPropertyValue(PropertyName.DiscountsTotal); }
            set { this.commerceEntity.SetPropertyValue(PropertyName.DiscountsTotal, value ?? null); }
        }

        /// <summary>
        /// Gets or sets the date created.
        /// </summary>
        /// <value>The date created.</value>
        public DateTime? DateCreated
        {
            get { return this.commerceEntity.DateCreated; }
            set { this.commerceEntity.DateCreated = value; }
        }

        /// <summary>
        /// Gets or sets the date modified.
        /// </summary>
        /// <value>The date modified.</value>
        public DateTime? DateModified
        {
            get { return this.commerceEntity.DateModified; }
            set { this.commerceEntity.DateModified = value; }
        }

        /// <summary>
        /// Gets or sets the total handling charge for the current basket.
        /// </summary>
        /// <value>The handling total.</value>
        public decimal? HandlingTotal
        {
            get { return (decimal?)this.commerceEntity.GetPropertyValue(PropertyName.HandlingTotal); }
            set { this.commerceEntity.SetPropertyValue(PropertyName.HandlingTotal, value ?? null); }
        }

        /// <summary>
        /// Gets or sets the sum of the discount amounts applied to line items.
        /// </summary>
        /// <value>The sum of line item discounts.</value>
        public decimal? LineItemDiscountsTotal
        {
            get { return (decimal?)this.commerceEntity.GetPropertyValue(PropertyName.LineItemDiscountsTotal); }
            set { this.commerceEntity.SetPropertyValue(PropertyName.LineItemDiscountsTotal, value ?? null); }
        }

        /// <summary>
        /// Gets or sets the line item collection associated with the current basket.
        /// </summary>
        /// <value>The line items.</value>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Collection setting is required")]
        public CommerceRelationshipList LineItems
        {
            get { return this.commerceEntity.GetPropertyValue(RelationshipName.LineItems) as CommerceRelationshipList; }
            set { this.commerceEntity.SetPropertyValue(RelationshipName.LineItems, value); }
        }

        /// <summary>
        /// Gets or sets the name of the basket.
        /// </summary>
        /// <value>The name of the Basket.</value>
        public string Name
        {
            get { return this.commerceEntity.GetPropertyValue(PropertyName.Name) as string; }
            set { this.commerceEntity.SetPropertyValue(PropertyName.Name, value); }
        }

        /// <summary>
        /// Gets or sets a string value that uniquely identifies a placed order.
        /// </summary>
        /// <value>The order number.</value>
        public string OrderNumber
        {
            get { return this.commerceEntity.GetPropertyValue(PropertyName.OrderNumber) as string; }
            set { this.commerceEntity.SetPropertyValue(PropertyName.OrderNumber, value); }
        }

        /// <summary>
        /// Gets or sets the payment collection for the current basket.
        /// </summary>
        /// <value>The payments.</value>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Collection setting is required")]
        public CommerceRelationshipList Payments
        {
            get { return this.commerceEntity.GetPropertyValue(RelationshipName.Payments) as CommerceRelationshipList; }
            set { this.commerceEntity.SetPropertyValue(RelationshipName.Payments, value); }
        }

        /// <summary>
        /// Gets or sets the marketing promotion codes associated with the current basket.
        /// </summary>
        /// <value>The promo codes.</value>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Collection setting is required")]
        public CommerceRelationshipList RequestedPromoCodes
        {
            get { return this.commerceEntity.GetPropertyValue(RelationshipName.RequestedPromoCodes) as CommerceRelationshipList; }
            set { this.commerceEntity.SetPropertyValue(RelationshipName.RequestedPromoCodes, value); }
        }

        /// <summary>
        /// Gets or sets the shipment collection associated with the current basket.
        /// </summary>
        /// <value>The shipments.</value>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Collection setting is required")]
        public CommerceRelationshipList Shipments
        {
            get { return this.commerceEntity.GetPropertyValue(RelationshipName.Shipments) as CommerceRelationshipList; }
            set { this.commerceEntity.SetPropertyValue(RelationshipName.Shipments, value); }
        }

        /// <summary>
        /// Gets or sets the sum of the discount amounts applied to shipping.
        /// </summary>
        /// <value>The shipping total.</value>
        public decimal? ShippingDiscountsTotal
        {
            get { return (decimal?)this.commerceEntity.GetPropertyValue(PropertyName.ShippingDiscountsTotal); }
            set { this.commerceEntity.SetPropertyValue(PropertyName.ShippingDiscountsTotal, value ?? null); }
        }

        /// <summary>
        /// Gets or sets the total shipping charges associated with the current instance,
        /// after all the applicable shipping discounts have been applied.
        /// </summary>
        /// <value>The shipping total.</value>
        public decimal? ShippingTotal
        {
            get { return (decimal?)this.commerceEntity.GetPropertyValue(PropertyName.ShippingTotal); }
            set { this.commerceEntity.SetPropertyValue(PropertyName.ShippingTotal, value ?? null); }
        }

        /// <summary>
        /// Gets or sets the status of the current basket.
        /// </summary>
        /// <value>The status.</value>
        public string Status
        {
            get { return this.commerceEntity.GetPropertyValue(PropertyName.Status) as string; }
            set { this.commerceEntity.SetPropertyValue(PropertyName.Status, value); }
        }

        /// <summary>
        /// Gets or sets the sub total amount for the current basket.
        /// </summary>
        /// <value>The sub total.</value>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SubTotal", Justification = "Mapped from Commerce Server")]
        public decimal? SubTotal
        {
            get { return (decimal?)this.commerceEntity.GetPropertyValue(PropertyName.SubTotal); }
            set { this.commerceEntity.SetPropertyValue(PropertyName.SubTotal, value ?? null); }
        }

        /// <summary>
        /// Gets or sets the total tax associated with the current basket.
        /// </summary>
        /// <value>The tax total.</value>
        public decimal? TaxTotal
        {
            get { return (decimal?)this.commerceEntity.GetPropertyValue(PropertyName.TaxTotal); }
            set { this.commerceEntity.SetPropertyValue(PropertyName.TaxTotal, value ?? null); }
        }

        /// <summary>
        /// Gets or sets the sum total of sub total, shipping, handling and taxes for the current basket.
        /// </summary>
        /// <value>The total.</value>
        public decimal? Total
        {
            get { return (decimal?)this.commerceEntity.GetPropertyValue(PropertyName.Total); }
            set { this.commerceEntity.SetPropertyValue(PropertyName.Total, value ?? null); }
        }

        /// <summary>
        /// Gets or sets the user identifier of the shopper that the basket belongs to.
        /// </summary>
        /// <value>The sold to id.</value>
        public string UserId
        {
            get { return this.commerceEntity.GetPropertyValue(PropertyName.UserId) as string; }
            set { this.commerceEntity.SetPropertyValue(PropertyName.UserId, value); }
        }

        /// <summary>
        /// Gets or sets the targeting context.
        /// </summary>
        /// <value>Targeting Context.</value>
        /// <remarks>This is an optional property.</remarks>
        public CommerceRelationship TargetingContext
        {
            get { return this.commerceEntity.GetPropertyValue(RelationshipName.TargetingContext) as CommerceRelationship; }
            set { this.commerceEntity.SetPropertyValue(RelationshipName.TargetingContext, value); }
        }

        #endregion

        #region Implicit operators
        /// <summary>
        /// Toes the user profile.
        /// </summary>
        /// <param name="commerceEntity">The commerce entity.</param>
        /// <returns></returns>
        public static implicit operator Basket(CommerceEntity commerceEntity)
        {
            System.Diagnostics.Debug.Assert(Basket.ModelNameDefinition.Equals(commerceEntity.ModelName), "Unexpected model name");
            return new Basket(commerceEntity);
        }
        #endregion

        /// <summary>
        /// Required so that the Builders can convert to the Multi-channel Commerce Foundation CommerceEntity base type using <see cref="ModelName"/>
        /// </summary>
        /// <returns>This instances base type</returns>
        public CommerceEntity ToCommerceEntity()
        {
            return this.commerceEntity;
        }

        #region Property key names

        /// <summary>
        /// Class defining Properties of Items that may be related to a Basket.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "Needed for external access")]
        public static class RelationshipName
        {
            #region Constants
            /// <summary>
            /// The name of the Addresses property for an item related to a Basket.
            /// </summary>
            public const string Addresses = "Addresses";

            /// <summary>
            /// The name of the LineItems property for an item related to a Basket.
            /// </summary>
            public const string LineItems = "LineItems";

            /// <summary>
            /// The name of the Payments property for an item related to a Basket.
            /// </summary>
            public const string Payments = "Payments";

            /// <summary>
            /// The name of the PromoCodes property for an item related to a Basket.
            /// </summary>
            public const string RequestedPromoCodes = "RequestedPromoCodes";

            /// <summary>
            /// The name of the Shipments property for an item related to a Basket.
            /// </summary>
            public const string Shipments = "Shipments";

            /// <summary>
            /// The name of the TargetingContext property for an item related to a Basket.
            /// </summary>
            public const string TargetingContext = "TargetingContext";

            #endregion
        }

        /// <summary>
        /// Names of the properties exposed by this commerce entity.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "Needed for external access"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors", Justification = "Cannot make it private as it cannot be derived from")]
        public class PropertyName : CommerceEntity.PropertyName
        {
            #region Constants

            /// <summary>
            /// The name of the BasketLevelDiscountsTotal property for a Basket.
            /// </summary>
            public const string BasketLevelDiscountsTotal = "BasketLevelDiscountsTotal";

            /// <summary>
            /// The name of the BasketType property for a Basket.
            /// </summary>
            public const string BasketType = "BasketType";

            /// <summary>
            /// The name of the BillingCurrency property for a Basket.
            /// </summary>
            public const string BillingCurrency = "BillingCurrency";

            /// <summary>
            /// The name of the DiscountsTotal property for a Basket.
            /// </summary>
            public const string DiscountsTotal = "DiscountsTotal";

            /// <summary>
            /// The name of the HandlingTotal property for a Basket.
            /// </summary>
            public const string HandlingTotal = "HandlingTotal";

            /// <summary>
            /// The name of the LineItemDiscountsTotal property for a Basket.
            /// </summary>
            public const string LineItemDiscountsTotal = "LineItemDiscountsTotal";

            /// <summary>
            /// The name of the Name property for a Basket.
            /// </summary>
            public const string Name = "Name";

            /// <summary>
            /// The name of the OrderNumber property for a Basket.
            /// </summary>
            public const string OrderNumber = "OrderNumber";

            /// <summary>
            /// The name of the ShippingDiscountsTotal property for a Basket.
            /// </summary>
            public const string ShippingDiscountsTotal = "ShippingDiscountsTotal";

            /// <summary>
            /// The name of the ShippingTotal property for a Basket.
            /// </summary>
            public const string ShippingTotal = "ShippingTotal";

            /// <summary>
            /// The name of the Status property for a Basket.
            /// </summary>
            public const string Status = "Status";

            /// <summary>
            /// The name of the SubTotal property for a Basket.
            /// </summary>
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SubTotal", Justification = "Mapped from Commerce Server")]
            public const string SubTotal = "SubTotal";

            /// <summary>
            /// The name of the TaxTotal property for a Basket.
            /// </summary>
            public const string TaxTotal = "TaxTotal";

            /// <summary>
            /// The name of the Total property for a Basket.
            /// </summary>
            public const string Total = "Total";

            /// <summary>
            /// The name of the UserId property for a Basket.
            /// </summary>
            public const string UserId = "UserId";

            #endregion
        }

        #endregion
    }
}
